package client;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.StringTokenizer;

public abstract class AbstractScriptedTripTest<ClientSession,ManagerSession> {

	/**
	 * Add a quote for a given car type to the session.
	 * 
	 * @param 	session 
	 * 			the session to add the reservation to
	 * @param 	start 
	 * 			start time of the reservation
	 * @param 	end
	 * 			end time of the reservation
	 * @param 	carType 
	 * 			type of car to be reserved
	 * @param 	carRentalName 
	 * 			name of the rental company by which the reservation should be done
	 * 
	 * @throws 	Exception
	 * 			if things go wrong, throw exception
	 */
	protected abstract void addQuoteToSession(ClientSession session,
			Date start, Date end, String carType, String carRentalName) throws Exception;
	
	/**
	 * Confirm the quotes in the given session.
	 * 
	 * @param 	session 
	 * 			the session to finalize
	 * 
	 * @throws 	Exception
	 * 			if things go wrong, throw exception
	 */
	protected abstract void confirmQuotes(ClientSession session) throws Exception;

	/**
	 * Create a new client session for the user with the given name.
	 * 
	 * @param 	name 
	 * 			name of the client owning this session
	 * @return 	the new client session
	 * 
	 * @throws 	Exception
	 * 			if things go wrong, throw exception
	 */
	protected abstract ClientSession getNewClientSession(String name) throws Exception;

	/**
	 * Create a new manager session for the user with the given name.
	 * 
	 * @param 	name 
	 * 			name of the client owning this session
	 * @return 	the new manager session
	 * 
	 * @throws 	Exception
	 * 			if things go wrong, throw exception
	 */
	protected abstract ManagerSession getNewManagerSession(String name, String carRentalName) throws Exception;

	/**
	 * Get the number of reservations for a particular car type.

	 * @param 	ms 
	 * 			manager session
	 * @param 	carType 
	 * 			name of the car type
	 * @return 	number of reservations for this car type
	 * 
	 * @throws 	Exception
	 * 			if things go wrong, throw exception
	 */
	protected abstract int getReservationsForCarType(ManagerSession ms, String company, String carType) throws Exception;
	
	//date format to parse dates from file
	private static final DateFormat datef = new SimpleDateFormat("d/M/y");
	//name of the file containing the test script 
	private String scriptFile;
	//open sessions
	private HashMap<String, ClientSession> sessions = new HashMap<String, ClientSession>();
	
	public AbstractScriptedTripTest(String scriptFile) {
		this.scriptFile = scriptFile;
	}
	
	public void run()throws Exception {
		//read script from JAR
		BufferedReader in = new BufferedReader(new InputStreamReader(AbstractScriptedTripTest.class.getClassLoader().getResourceAsStream(scriptFile)));
		int lnr = 0;
		//while we have lines
		while (in.ready()) {
			lnr++;
			
			//read line
			String line = in.readLine();
			//tokenize
			StringTokenizer scriptReader = new StringTokenizer(line, " ");
			
			//get command and name
			String name = scriptReader.nextToken();
			String cmd = scriptReader.nextToken();
			
			//dispatch according to command
			if(cmd.contains("S"))
				sessions.put(name, getNewClientSession(name));
			else if(cmd.contains("M"))
				check(name,scriptReader);
			else{
				ClientSession session = sessions.get(name);
				if(session==null)
					throw new IllegalArgumentException("script broken: no session" +  line +" on line " + lnr );
				if(cmd.contains("B")){
					Date start = datef.parse(scriptReader.nextToken());
					Date end = datef.parse(scriptReader.nextToken());
					String type = scriptReader.nextToken();
					String agent = scriptReader.nextToken();
					Exception be = null;
					try {
						addQuoteToSession(session, start, end,
								type, agent);
					} catch (Exception e) {
						be = e;
					}
					boolean shouldfail = cmd.contains("C");
					if(be == null && shouldfail)
						System.err.println("command should have failed: " + line +" on line " + lnr );
					if(be != null && !shouldfail){
						System.err.println("commandfailed: " + line +" on line " + lnr );
						be.printStackTrace();
					}
				}else if(cmd.contains("F")){
					Exception be = null;
					try {
						confirmQuotes(session);
					} catch (Exception e) {
						be = e;
					}
					boolean shouldfail = cmd.contains("C");
					if(be == null && shouldfail)
						System.err.println("command should have failed: " + line +" on line " + lnr );
					if(be != null && !shouldfail){
						System.err.println("commandfailed: " + line +" on line " + lnr );
						be.printStackTrace();
					}
				}else throw new IllegalArgumentException("unknown command"  + line +" on line " + lnr );
			}
		}	
	}
	
	private void check(String name, StringTokenizer scriptReader) throws Exception {
		ManagerSession rental = getNewManagerSession(name,name);
		while(scriptReader.hasMoreTokens()){
			String pars = scriptReader.nextToken();
			String[] pair = pars.split(":");
			int nr = getReservationsForCarType(rental,name,pair[0]);
			if(Integer.parseInt(pair[1]) == nr){
				System.out.println(name + " has correct totals " + pars + " " + nr);
			}else{
				System.err.println(name + " has wrong totals " + pars +" " + nr );
			}
		}
	}
}
